package com.kxr.literetrofit.network.ssl;

import com.kxr.literetrofit.network.ApiService;

import org.apache.http.conn.ssl.AbstractVerifier;

import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Locale;
import java.util.regex.Pattern;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;

/***
 * 证书来源校验
 * @author  Kongxr
 **/
public class SafeHouseHostVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        try {
            Certificate[] certs = session.getPeerCertificates();
            X509Certificate x509 = (X509Certificate) certs[0];
            String[] cns = AbstractVerifier.getCNs(x509);
            String[] subjectAlts = AbstractVerifier.getDNSSubjectAlts(x509);
            verifierHostName(hostname, cns, subjectAlts);
            return true;
        } catch(SSLException e) {
            return false;
        }
    }
    private void verifierHostName(String host, String[] cns, String[] subjectAlts) throws SSLException {
        LinkedList<String> names = new LinkedList<>();
        names.add("safehouse.csizg.com");			//服务器证书未填写域名信息
        names.add("pos.csizg.com");
        names.add(ApiService.HOST);
        if(cns != null && cns.length > 0 && cns[0] != null) {
            names.add(cns[0]);
        }
        if(subjectAlts != null) {
            for (String subjectAlt : subjectAlts) {
                if (subjectAlt != null) {
                    names.add(subjectAlt);
                }
            }
        }
        if(names.isEmpty()) {
            String msg = "Certificate for <" + host + "> doesn't contain CN or DNS subjectAlt";
            throw new SSLException(msg);
        }
        StringBuffer buf = new StringBuffer();
        String hostName = host.trim().toLowerCase(Locale.ENGLISH);
        boolean match = false;
        for(Iterator<String> it = names.iterator(); it.hasNext();) {
            String cn = it.next();
            cn = cn.toLowerCase(Locale.ENGLISH);
            buf.append(" <");
            buf.append(cn);
            buf.append('>');
            if(it.hasNext()) {
                buf.append(" OR");
            }
            boolean doWildcard = cn.startsWith("*.") &&
                    cn.indexOf('.', 2) != -1 &&
                    AbstractVerifier.acceptableCountryWildcard(cn) &&
                    !isIPv4Address(host);

            if(doWildcard) {
                match = hostName.endsWith(cn.substring(1));
                if(match) {
                    match = AbstractVerifier.countDots(hostName) == AbstractVerifier.countDots(cn);
                }
            } else {
                match = hostName.equals(cn);
            }
            if(match) {
                break;
            }
        }
        if(!match) {
            throw new SSLException("hostname in certificate didn't match: <" + host + "> !=" + buf);
        }
    }

    private boolean isIPv4Address(final String input) {
        return IPV4_PATTERN.matcher(input).matches();
    }
    private final Pattern IPV4_PATTERN = Pattern.compile(
            "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
}